package org.meb.conquest.web.ctrl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ResourceBundle;

import javax.annotation.PostConstruct;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;

import lombok.Getter;
import lombok.Setter;

import org.apache.commons.lang.StringUtils;
import org.meb.conquest.db.dao.Query;
import org.meb.conquest.db.dao.Query.Mode;
import org.meb.conquest.db.model.Card;
import org.meb.conquest.db.model.CardSet;
import org.meb.conquest.db.model.CardType;
import org.meb.conquest.db.model.Domain;
import org.meb.conquest.db.model.Faction;
import org.meb.conquest.db.query.CardQuery;
import org.meb.conquest.db.query.Sorting.Direction;
import org.meb.conquest.web.AutoCompleteCache;
import org.meb.conquest.web.AutoCompleteItem;
import org.meb.conquest.web.services.SearchService;
import org.meb.conquest.web.services.User;
import org.primefaces.component.datagrid.DataGrid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Named
@ViewScoped
@Getter
@Setter
public class CardSearchCtrl implements Serializable {

	private static final Logger log = LoggerFactory.getLogger(CardSearchCtrl.class);
	private static final long serialVersionUID = 3973801993975443027L;

	private static final String SEARCH_TARGET_NAME = "name";
	private static final String SEARCH_TARGET_TRAIT = "trait";
	private static final String SEARCH_TARGET_TEXT = "text";

	@Inject
	private User user;

	@Inject
	private SearchService searchService;

	@Inject
	private AutoCompleteCache autoCompleteCache;

	@Inject
	private LocaleCtrl localeCtrl;

	// text filter
	private String queryText;
	private LinkedHashMap<String, String> searchTargetItems;
	private List<String> selectedSearchTargets;

	// checkbox filter
	private LinkedHashMap<String, String> factionItems;
	private List<String> selectedFactions;
	private LinkedHashMap<String, String> cardTypeItems;
	private List<String> selectedCardTypes;

	// autocomplete filters
	private List<CardSet> selectedCardSets;
	private List<Domain> selectedSortItems;

	// other
	private String unique = "0";
	private String localized = "0";
	private String corrected = "0";
	private boolean showCorrections;

	// results
	private List<Card> cards;
	private List<CardItem> cardItems;
	private List<CardItem[]> cardItemsArray;
	private Integer cardItemsCount;

	// bindings
	private DataGrid cardsDataGrid;

	private boolean allFactions;
	private boolean allPlayerCardTypes;
	private boolean allEncounterCardTypes;
	private boolean allOtherCardTypes;

	private String sortItemsTooltip;

	@PostConstruct
	public void initialize() {
		prepareCheckboxFilters();
		prepareAutoCompleteFiters();
		prepareSearchTargets();
	}

	private void prepareSearchTargets() {
		ResourceBundle bundle = getResourceBundle();
		searchTargetItems = new LinkedHashMap<String, String>();
		searchTargetItems.put(bundle.getString("name"), SEARCH_TARGET_NAME);
		searchTargetItems.put(bundle.getString("trait"), SEARCH_TARGET_TRAIT);
		searchTargetItems.put(bundle.getString("text"), SEARCH_TARGET_TEXT);
		if (selectedSearchTargets == null) {
			selectedSearchTargets = new ArrayList<String>();
			for (String value : searchTargetItems.values()) {
				selectedSearchTargets.add(value);
			}
		}
	}

	private void prepareCheckboxFilters() {
		HashMap<String, String> domainsMap = new HashMap<String, String>();
		List<Domain> domains = searchService.find(new Domain());
		for (Domain domain : domains) {
			domainsMap.put(domain.getValue(), domain.getDescription());
		}

		factionItems = new LinkedHashMap<String, String>();
		if (selectedFactions == null) {
			selectedFactions = new ArrayList<String>();
		}
		for (Faction faction : Faction.values()) {
			factionItems.put(domainsMap.get(faction.toString()), faction.toString());
		}

		if (selectedCardTypes == null) {
			selectedCardTypes = new ArrayList<String>();
		}
		cardTypeItems = new LinkedHashMap<String, String>();
		for (CardType type : CardType.values()) {
			cardTypeItems.put(domainsMap.get(type.toString()), type.toString());
		}
	}

	private void prepareAutoCompleteFiters() {
		restoreCardSetAutoCompleteState();
		restoreSortItemsAutoCompleteState();
	}

	private void restoreCardSetAutoCompleteState() {
		if (selectedCardSets != null) {
			for (int i = 0; i < selectedCardSets.size(); i++) {
				CardSet cardSet = new CardSet(selectedCardSets.get(i).getId());
				cardSet = searchService.findUnique(cardSet);
				selectedCardSets.set(i, cardSet);
				autoCompleteCache.add(AutoCompleteItem.create(cardSet));
			}
		}
	}

	private void restoreSortItemsAutoCompleteState() {
		if (selectedSortItems != null) {
			for (int i = 0; i < selectedSortItems.size(); i++) {
				Domain domain = new Domain(selectedSortItems.get(i).getId());
				domain = searchService.findUnique(domain);
				domain.setValue(selectedSortItems.get(i).getValue());
				selectedSortItems.set(i, domain);
				autoCompleteCache.add(AutoCompleteItem.create(domain));
			}
		}
	}

	// @SuppressWarnings("unused")
	// private void languageChangeEventHandler(@Observes LanguageChangeEvent
	// event) {
	// autoCompleteCache.clear();
	// prepareCheckboxFilters();
	// prepareAutoCompleteFiters();
	// prepareSearchTargets();
	// cards = null;
	// cardItems = null;
	// cardItemsCount = null;
	// if (!isCorrectionsAvailable()) {
	// corrected = "0";
	// showCorrections = false;
	// }
	// }

	public boolean isCorrectionsAvailable() {
		String language = localeCtrl.getLanguage();
		return "pl".equals(language) || "en".equals(language);
	}

	private ResourceBundle getResourceBundle() {
		FacesContext ctx = FacesContext.getCurrentInstance();
		ResourceBundle bundle = ctx.getApplication().getResourceBundle(ctx, "msgs");
		return bundle;
	}

	public void search() {
		CardQuery query = new CardQuery();
		query.setText(queryText);
		query.setSearchInName(selectedSearchTargets.contains(SEARCH_TARGET_NAME));
		query.setSearchInTrait(selectedSearchTargets.contains(SEARCH_TARGET_TRAIT));
		query.setSearchInText(selectedSearchTargets.contains(SEARCH_TARGET_TEXT));

		List<Faction> factionEnums = new ArrayList<Faction>();
		for (String faction : selectedFactions) {
			factionEnums.add(Faction.valueOf(faction));
		}
		query.setFactions(factionEnums);

		List<CardType> cardTypeEnums = new ArrayList<CardType>();
		for (String cardType : selectedCardTypes) {
			cardTypeEnums.add(CardType.valueOf(cardType));
		}
		query.setCardTypes(cardTypeEnums);

		HashSet<Long> ids;
		if (selectedCardSets != null) {
			ids = new HashSet<Long>();
			for (CardSet cardSet : selectedCardSets) {
				ids.add(cardSet.getId());
			}
			query.setCardSetIds(new ArrayList<Long>(ids));
		}

		query.setUnique(triStateToBoolean(unique));
		query.setLocalized(triStateToBoolean(localized));
		query.setCorrected(triStateToBoolean(corrected));

		// sorting
		if (selectedSortItems != null) {
			for (Domain sortItem : selectedSortItems) {
				String[] tokens = sortItem.getValue().split("#");
				if (tokens[0] != null && tokens[1] != null) {
					Direction direction = Direction.valueOf(tokens[1].toUpperCase());
					query.getSorting().setSorting(tokens[0], direction);
				} else {
					log.warn("Unable to set sorting: property: {}, direction: {}", tokens[0],
							tokens[1]);
				}
			}
		} else {
			query.getSorting().setSortingAsc("crstReleaseDate");
			query.getSorting().setSortingAsc("name");
		}

		// search
		cards = searchService.findCards(query);
		cardItems = new ArrayList<CardItem>();
		for (Card card : cards) {
			cardItems.add(new CardItem(card));
		}
		cardItemsCount = cardItems.size();

		// back to first page
		if (cardsDataGrid != null) {
			cardsDataGrid.setFirst(0);
		}
	}

	private Boolean triStateToBoolean(String stringValue) {
		Boolean value;
		if ("1".equals(stringValue)) {
			value = true;
		} else if ("2".equals(stringValue)) {
			value = false;
		} else {
			value = null;
		}
		return value;
	}

	public void reset() {
		queryText = null;
		selectedFactions.clear();
		selectedCardTypes.clear();
		selectedCardSets = null;
		selectedSearchTargets.clear();
		for (String value : searchTargetItems.values()) {
			selectedSearchTargets.add(value);
		}
		unique = "0";
		localized = "0";
		corrected = "0";
		showCorrections = false;
	}

	public List<CardSet> completeCardSet(String value) {
		String likeValue = "%" + value + "%";

		CardSet example = new CardSet();
		example.setName(likeValue);
		example.setCycleName(likeValue);
		List<CardSet> results = searchService.find(new Query<CardSet>(example, Mode.OR));
		for (CardSet result : results) {
			autoCompleteCache.add(AutoCompleteItem.create(result));
			result.setName(markText(result.getName(), value));
			result.setCycleName(markText(result.getCycleName(), value));
		}
		return results;
	}

	public List<Domain> completeSortItem(String value) {
		String likeValue = "%" + value + "%";

		Domain example = new Domain("CARDSORTPROP");
		example.setDescription(likeValue);
		List<Domain> results = searchService.find(example);
		List<Domain> resultsAscDesc = new ArrayList<Domain>();
		for (Domain result : results) {
			String marked = markText(result.getDescription(), value);

			Domain asc = new Domain(result.getDomain());
			asc.setId(result.getId());
			asc.setValue(result.getValue() + "#asc");
			asc.setDescription(marked + "\u25b2");
			autoCompleteCache.add(AutoCompleteItem.create(asc));
			resultsAscDesc.add(asc);

			Domain desc = new Domain(result.getDomain());
			desc.setId(result.getId());
			desc.setValue(result.getValue() + "#desc");
			desc.setDescription(marked + "\u25bc");
			autoCompleteCache.add(AutoCompleteItem.create(desc));
			resultsAscDesc.add(desc);
		}
		return resultsAscDesc;
	}

	private String markText(String text, String search) {
		int beginIdx = StringUtils.indexOfIgnoreCase(text, search);
		if (beginIdx != -1) {
			int endIdx = beginIdx + search.length();
			text = text.substring(0, beginIdx) + "<span class=\"autocomplete-text-match\">"
					+ text.substring(beginIdx, endIdx) + "</span>" + text.substring(endIdx);
		}
		return text;
	}

//	private void toggleAllCardTypes(boolean selected, Collection<String> availableValues,
//			Collection<String> selectedValues) {
//		try {
//			Thread.sleep(2000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		if (selected) {
//			for (String item : availableValues) {
//				if (!selectedValues.contains(item)) {
//					selectedValues.add(item);
//				}
//			}
//		} else {
//			selectedValues.clear();
//		}
//	}

	private void clearSearchCriteria() {
		queryText = null;
		selectedCardSets = null;
		selectedCardTypes.clear();
		allPlayerCardTypes = false;
		allEncounterCardTypes = false;
		allOtherCardTypes = false;
	}

	public void searchWithType(CardType typeCode) {
		clearSearchCriteria();
		// if (typeCode.isPlayerDeck()) {
		// selectedPlayerCardTypes.add(typeCode.toString());
		// } else if (typeCode.isEncounterDeck()) {
		// selectedEncounterCardTypes.add(typeCode.toString());
		// } else if (typeCode.isQuestDeck()) {
		// selectedOtherCardTypes.add(typeCode.toString());
		// }
		search();
	}

	public void searchWithTrait(String trait) {
		clearSearchCriteria();
		trait = StringUtils.chomp(trait, ".");
		queryText = trait;
		selectedSearchTargets.clear();
		selectedSearchTargets.add(SEARCH_TARGET_TRAIT);
		search();
	}

	public void searchWithFaction(Faction factionCode) {
		clearSearchCriteria();
		selectedFactions.add(factionCode.toString());
		search();
	}

	public void searchWithCardSet(Long crstId) {
		clearSearchCriteria();
		selectedCardSets = new ArrayList<CardSet>();
		selectedCardSets.add(new CardSet(crstId));
		restoreCardSetAutoCompleteState();
		search();
	}
}